package com.edol.painting.painting;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.os.Environment;
import android.support.annotation.RawRes;
import android.util.Log;

import com.edol.painting.R;
import com.edol.painting.utils.BitmapUtils2;
import com.doodle.common.utils.Utils;
import com.edol.painting.utils.Constant;
import com.edol.painting.utils.PainterUtils;

import java.io.DataInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Random;


public class TextureBrush extends Brush {

    public static final int DITHER_TYPE_NORMAL = 0;
    public static final int DITHER_TYPE_RANDOM = 1;

    public static final int DIRECTION_TYPE_NORMAL = 0;

    public static final int DIRECTION_TYPE_RANDOM = 1;
    public static final int DIRECTION_TYPE_TANGENT = 2;
    public static final int DIRECTION_TYPE_TANGENT_RANDOM = 3;

    protected int mDitherType = DITHER_TYPE_NORMAL;
    protected int mDirectionType = DIRECTION_TYPE_NORMAL;
    protected float mDitherFactor = 0;
    protected int mDirectionValue = 0;
    protected float mStepFactor = 0;

    protected Bitmap mMask;
    protected int mMaskWidth;
    protected int mMaskHeight;
    protected Matrix mMatrix;
    protected Random mRandom;

    protected int mDitherRadius = 0;
    protected int mCurParticleIndex = 0;
    protected int mBrushMaxStep = 0;
    private float mDrawRadius;

    public TextureBrush(Context context, int type) {
        super(context, type);
        mRandom = new Random();
        mMatrix = new Matrix();
        mBrushMaxStep = Constant.BRUSH_WIDTH_SEEKBAR_MAX / 2;
    }

    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        mDitherRadius = Math.max(2, Math.round(mWidth * mDitherFactor));
        mStep = Math.max(3, Math.round(mWidth * mStepFactor));

        mDrawRadius = mWidth * 2.0f + Math.round(mWidth * mDitherFactor);
    }

    @Override
    public void setColor(int color) {
        super.setColor(color);
        mPaint.setColorFilter(new PorterDuffColorFilter(mColor, PorterDuff.Mode.SRC_ATOP));
    }

    public void setDitherType(int ditherType, int ditherValue) {
        mDitherType = ditherType;
        mDitherFactor = Math.max(0, ditherValue);
    }

    public void setDirectionType(int directionType, int directionValue) {
        mDirectionType = directionType;
        mDirectionValue = Math.max(1, directionValue);
    }

    @Override
    protected void drawParticle(Canvas canvas, Particle particle, Rect bounds) {
        float x = particle.x;
        float y = particle.y;
        mPaint.setAlpha(particle.alpha);
        int randNum = PainterUtils.getRandNum(mCurParticleIndex);

        if (DITHER_TYPE_RANDOM == mDitherType) {
            int ditherValue = randNum % mDitherRadius;
            x += ditherValue;
            y += ditherValue;
        }
        float width = particle.width;
        float startX = x - width;
        float startY = y - width;

        if (DIRECTION_TYPE_RANDOM == mDirectionType) {
            int degree = randNum % mDirectionValue;
            mMatrix.setRotate(degree, mWidth, mWidth);
            mMatrix.postScale((width * 2 + 1) / (float) mMaskWidth, (width * 2 + 1) / (float) mMaskWidth);
        } else if (DIRECTION_TYPE_TANGENT == mDirectionType) {
            int degree = (int) particle.angle;
            mMatrix.setRotate(degree, mWidth, mWidth);
            mMatrix.postScale((width * 2 + 1) / (float) mMaskWidth, (width * 2 + 1) / (float) mMaskWidth);
        } else if (DIRECTION_TYPE_TANGENT_RANDOM == mDirectionType) {
            int degree = (int) (particle.angle + randNum % mDirectionValue);
            mMatrix.setRotate(degree, mWidth, mWidth);
            mMatrix.postScale((width * 2 + 1) / (float) mMaskWidth, (width * 2 + 1) / (float) mMaskWidth);
        } else {
            mMatrix.setScale((width * 2 + 1) / (float) mMaskWidth, (width * 2 + 1) / (float) mMaskWidth);
        }
        mMatrix.postTranslate(startX, startY);
		//canvas.drawBitmap(mMask, mMatrix, mPaint);

        bounds.union((int) (x - mDrawRadius), (int) (y - mDrawRadius), (int) (x + mDrawRadius), (int) (y + mDrawRadius));
        mLayerCanvas.save(Canvas.CLIP_SAVE_FLAG);
        mLayerCanvas.clipRect(bounds);
        mLayerCanvas.drawBitmap(mMask, mMatrix, mPaint);
        mLayerCanvas.restore();

        ++mCurParticleIndex;
    }

    @Override
    public void drawRoute(Canvas canvas, Particle particle, Rect bounds) {
        if (canvas != null && particle != null && bounds != null) {
            mRoute.add(particle);
            int count = mRoute.size();
            if (count < 2) {
                return;
            }
            Route result = null;
            if (count == 2) {
                Particle p0 = mRoute.get(0);
                Particle p1 = mRoute.get(1);
                if (mEffectType == EFFECT_TYPE_PRESSURE) {
                    p1.width = Utils.clamp(p0.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
                    p1.alpha = mAlpha;
                } else if (mEffectType == EFFECT_TYPE_FADE) {
                    p1.width = mWidth;
                    p1.alpha = 0;
                } else if (mEffectType == EFFECT_TYPE_PRESSURE_FADE) {
                    p1.width = Utils.clamp(p0.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
                    p1.alpha = 0;
                } else if (mEffectType == EFFECT_TYPE_LIGHT_FADE) {
                    p1.width = mWidth;
                    p1.alpha = mAlpha * 2 / 3;
                } else if (mEffectType == EFFECT_TYPE_LIGHT_PRESSURE_FADE) {
                    p1.width = Utils.clamp(p0.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
                    p1.alpha = mAlpha * 2 / 3;
                } else {
                    p1.width = mWidth;
                    p1.alpha = mAlpha;
                }
                Particle midPoint = Particle.getMidPoint(p0, p1);
                result = Route.calculateLinePoints(p0, midPoint, mStep);
            } else {
                Particle p0 = mRoute.get(count - 3);
                Particle p1 = mRoute.get(count - 2);
                Particle p2 = mRoute.get(count - 1);

                if (mEffectType == EFFECT_TYPE_PRESSURE) {
                    p2.width = Utils.clamp(p1.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
                } else if (mEffectType == EFFECT_TYPE_FADE) {
                    p2.width = mWidth;
                } else if (mEffectType == EFFECT_TYPE_PRESSURE_FADE) {
                    p2.width = Utils.clamp(p1.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
                } else if (mEffectType == EFFECT_TYPE_LIGHT_PRESSURE_FADE) {
                    p2.width = Utils.clamp(p1.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
                } else {
                    p2.width = mWidth;
                }
                p2.alpha = mAlpha;
                result = Route.calculateSmoothLinePoints(p0, p1, p2, mStep);
            }
            mCurParticleIndex = 0;
            doDraw(canvas, result, bounds);
        }


    }

    @Override
    public void endRoute(Canvas canvas, Particle particle, Rect bounds) {
        if (canvas != null && particle != null && bounds != null) {
            mRoute.add(particle);
            if (canvas != null && mRoute != null) {
                int count = mRoute.size();
                if (count >= Route.ROUTE_BEGIN_INDEX) {
                    Particle p0 = mRoute.get(count - 3);
                    Particle p1 = mRoute.get(count - 2);
                    Particle p2 = mRoute.get(count - 1);
                    if (mEffectType == EFFECT_TYPE_PRESSURE) {
                        p2.width = 0;
                        p2.alpha = mAlpha;
                    } else if (mEffectType == EFFECT_TYPE_FADE) {
                        p2.width = mWidth;
                        p1.alpha = p0.alpha / 2;
                        p2.alpha = -p1.alpha;
                    } else if (mEffectType == EFFECT_TYPE_PRESSURE_FADE) {
                        p2.width = 0;
                        p1.alpha = p0.alpha / 2;
                        p2.alpha = -p1.alpha;
                    } else if (mEffectType == EFFECT_TYPE_LIGHT_FADE) {
                        p2.width = mWidth;
                        p1.alpha = p0.alpha / 2;
                        p2.alpha = 0;
                    } else if (mEffectType == EFFECT_TYPE_LIGHT_PRESSURE_FADE) {
                        p2.width = 0;
                        p1.alpha = p0.alpha / 2;
                        p2.alpha = 0;
                    } else {
                        p2.width = mWidth;
                        p2.alpha = mAlpha;
                    }
                    Route result = Route.calculateSmoothLinePoints(p0, p1, p2, mStep);
                    mCurParticleIndex = 0;
                    doDraw(canvas, result, bounds);
                }
            }
        }
        mRoute.clear();
    }

    protected void loadBrush(int resId) {

        InputStream inputStream = null;
        DataInputStream dataInputStream = null;
        byte[] buffer = null;
        try {
            inputStream = mContext.getResources().openRawResource(resId);
            dataInputStream = new DataInputStream(inputStream);
            mMaskWidth = dataInputStream.readInt();
            mMaskHeight = dataInputStream.readInt();
            mEffectType = dataInputStream.readInt();
            mDitherType = dataInputStream.readInt();
            mDitherFactor = dataInputStream.readFloat();
            mDirectionType = dataInputStream.readInt();
            mDirectionValue = dataInputStream.readInt();
            mStepFactor = dataInputStream.readFloat();
            dataInputStream.readInt();
            if (mMaskWidth == 0) {
                mMaskWidth = 10;
            }
            if (mMaskHeight == 0) {
                mMaskHeight = 10;
            }
            buffer = new byte[mMaskWidth * mMaskHeight];
            dataInputStream.read(buffer);
            int[] pixels = new int[buffer.length];
            int alpha = 0;
            for (int i = 0; i < buffer.length; i++) {
                alpha = buffer[i];
                pixels[i] = (alpha & 0x000000ff) << 24;
            }
            mMask = Bitmap.createBitmap(pixels, mMaskWidth, mMaskHeight, Bitmap.Config.ARGB_8888);

            //test(resId);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            Utils.closeSilently(dataInputStream);
            Utils.closeSilently(inputStream);
        }
    }


    private void test (int resId) {
        String name = "";
        if (resId == R.raw.chalk) {
            name = "chalk";
        } else if (resId == R.raw.charcoal) {
            name = "charcoal";
        } else if (resId == R.raw.watercolor) {
            name = "watercolor";
        } else if (resId == R.raw.marker) {
            name = "marker";
        } else if (resId == R.raw.soft_charcoal) {
            name = "soft_charcoal";
        } else if (resId == R.raw.carbon) {
            name = "carbon";
        } else if (resId == R.raw.pencil) {
            name = "pencil";
        }
        String dir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/brush_" + name + ".png";
        BitmapUtils2.savePngImage(mMask, dir, 100, false, false);
    }
}